package com.hns.frame.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.AntPathRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;

import com.hns.can.base.entity.SysRoleEntity;
import com.hns.can.base.service.SysRoleResService;
import com.hns.can.base.service.SysRoleService;
import com.hns.can.base.vo.MenuVo;
import com.hns.frame.enumrate.MsgType;
import com.hns.frame.enumrate.SpecialFilterUrl;
import com.hns.frame.exception.BusinessException;
import com.hns.frame.util.ResponseUtil;
import com.hns.tool.pub.PubUtil;

/**
 * 资源管理类
 * @author:Fisher
 * @email:zhuangcaijin@126.com
 * @version Revision 2.0.0
 */
public class AppFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource, InitializingBean {
    
    static final Logger logger = LoggerFactory.getLogger(AppFilterInvocationSecurityMetadataSource.class);
    private SysRoleService sysRoleService;
    private SysRoleResService sysRoleResService;
    
    private boolean lowercaseComparisons = true;
    private static Map<String, Collection<ConfigAttribute>> MAP_RES = new HashMap<String, Collection<ConfigAttribute>>();
    
    private String matcher;                    // 规则标识
    private RequestMatcher requestMatcher;             // 匹配规则
                      
    /**
     * 初始化 
     * @param sysRoleService
     * @param sysRoleResService 
     * @author:Fisher
     * @email:zhuangcaijin@126.com
     */
    public AppFilterInvocationSecurityMetadataSource(SysRoleService sysRoleService, SysRoleResService sysRoleResService) {
        this.sysRoleService = sysRoleService;
        this.sysRoleResService = sysRoleResService;
        loadResourceDefine();
    }
    
    /**
     * 更新资源缓存
     * @author:Fisher
     * @email:zhuangcaijin@126.com
     */
    public void refresh() {
        loadResourceDefine();
    }
    
    /**
     * 加载资源与角色对应的权限集合
     * 格式：资源名name 权限有序list
     * @author:Fisher
     * @email:zhuangcaijin@126.com
     */
    private void loadResourceDefine() {
        MAP_RES.clear(); 
        Collection<ConfigAttribute> attr = new ArrayList<ConfigAttribute>();
        ConfigAttribute ca = null;
        
        final List<MenuVo> roleResList = sysRoleResService.getSysRoleResList();
        
        String resContext = "";
        if(PubUtil.isNotEmpty(roleResList)){
            for (MenuVo roleRes : roleResList) {
                attr = new ArrayList<ConfigAttribute>();
                ca = new SecurityConfig(roleRes.getRoleId());
                attr.add(ca);
                resContext = roleRes.getUrl().substring(1, roleRes.getUrl().length()) + "*";
                if (MAP_RES.containsKey(resContext)) {
                    Collection<ConfigAttribute> attrs = MAP_RES.get(resContext);
                    attrs.addAll(attr);
                    MAP_RES.put(resContext, attrs);
                } else {
                    MAP_RES.put(resContext, attr);
                }
            }
        }
    }
    
    /**
     * 获取所有角色
     * @return
     * @see org.springframework.security.access.SecurityMetadataSource#getAllConfigAttributes()
     * @author:Fisher
     * @email:zhuangcaijin@126.com
     */
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
        
        final List<SysRoleEntity> list_role = sysRoleService.findAll();
        for (SysRoleEntity role : list_role) {
            allAttributes.add(new SecurityConfig(role.getName()));
        }
        return allAttributes;
    }
    
    /**
     * 资源过滤
     * @param filter    
     * @return  如果认证通过返回角色集合
     * @see org.springframework.security.access.SecurityMetadataSource#getAttributes(java.lang.Object)
     * @author:Fisher
     * @email:zhuangcaijin@126.com
     */
    public Collection<ConfigAttribute> getAttributes(Object filter) {
        HttpServletRequest request = ((FilterInvocation) filter).getRequest();
        HttpServletResponse response = ((FilterInvocation) filter).getResponse();
        Iterator<String> iterator = MAP_RES.keySet().iterator();
        for(SpecialFilterUrl item : SpecialFilterUrl.values()){
            //TODO 待修改按钮权限修改
            //if (new AntPathRequestMatcher(item.getUrl()).matches(request)) {
            if (new AntPathRequestMatcher(item.getUrl()).matches(request) && (
                    !request.getRequestURI().contains("/updateSysRole.htm") && !request.getRequestURI().contains("/delSysRole.htm") ) ) {
                return null;
            }
        }
        
        while (iterator.hasNext()) {
            String uri = iterator.next();
            if ("ant".equals(matcher.toLowerCase())) {
                requestMatcher = new AntPathRequestMatcher(uri);
            }
            if (requestMatcher.matches(request)) {
                return MAP_RES.get(uri);
            }
        }
        
        ResponseUtil.error("您无法访问，请联系系统管理员!", "", request, response);
        logger.error(request.getRequestURL() + "访问无效");
        throw new BusinessException(MsgType.ERROR.getFlag(), request.getRequestURL() + "访问无效");
    }
    
    public boolean supports(Class<?> clazz) {
        return true;
    }
    
    public void afterPropertiesSet() throws Exception {
    }
    
    public void setMatcher(String matcher) {
        this.matcher = matcher;
    }
    
    public boolean isLowercaseComparisons() {
        return lowercaseComparisons;
    }
    
    public void setLowercaseComparisons(boolean lowercaseComparisons) {
        this.lowercaseComparisons = lowercaseComparisons;
    }

}
